/*
 * GEuropeanRankingUpdater.java
 * Created on 13 avr. 08
 *
 * Glacéo Internet Platform
 * http://sourceforge.net/projects/chm/
 *
 * Copyright (c) 2005-2008, Jens and Jan Janke (VirtualHockey Project)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hockeo.vhbase.seasoncycle.services;


import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;

import com.hockeo.vhbase.data.dao.IGDao;
import com.hockeo.vhbase.data.dao.assoc.GNationalAssociation;
import com.hockeo.vhbase.data.dao.club.GClub;
import com.hockeo.vhbase.data.dao.club.GClubHistoryRecord;
import com.hockeo.vhbase.data.dao.season.GSeason;
import com.hockeo.vhbase.data.dao.standings.GECRStandings;
import com.hockeo.vhbase.data.dao.standings.GENRStandings;

/**
 * Service class which is responsible for updating the European Club and Nations rankings
 * at the end of each season.
 *
 * @version $Id$
 * @author jjanke
 */
public class GEuropeanRankingUpdater
{
  private final IGDao               d_dao;
  private final GSeason                  d_season;
  private final GSeason                  d_seasonPrev;
  private final ArrayList<GECRStandings> d_listAllECRStandings; // all ECR standings created for the current season
  private final ArrayList<GENRStandings> d_listAllENRStandings; // all ENR standings created for the current season

  /**
   * Initializes the European Ranking Updater.
   *
   * @param dao the DAO to use for all database interactions.
   */
  public GEuropeanRankingUpdater( final IGDao dao, final GSeason season )
  {
    d_dao = dao;
    d_season = season;
    d_listAllECRStandings = new ArrayList<GECRStandings>();
    d_listAllENRStandings = new ArrayList<GENRStandings>();

    // Get previous season (may be null, if we are in the first season)
    //
    d_seasonPrev = d_dao.findEntity( GSeason.class, d_season.getId() - 1 );
  }

  /**
   * Creates rankings for all clubs of the given national association. The club specific
   * and the nation standings are both updated in one go.
   *
   * @param assoc the association for which to update the European rankings
   */
  public void updateRankings( GNationalAssociation assoc )
  {
    List<GECRStandings> listEcrLastYear = null;
    List<GClubHistoryRecord> listClubHistoryRecords = null;
    LinkedHashMap<GClub, GECRStandings> mapEcrNew = new LinkedHashMap<GClub, GECRStandings>();
    GENRStandings enrLastYear = null;
    GENRStandings enrNew = null;

    // Load all last year's standings objects for the given association
    //
    if ( d_seasonPrev != null )
    {
      listEcrLastYear = d_dao.findECRStandings( d_seasonPrev, assoc );
      enrLastYear = d_dao.findENRStanding( d_seasonPrev, assoc );
    }

    // Load all club/contest history records where a club has acquired ECR points in the
    // current season
    //
    listClubHistoryRecords = d_dao.findClubHistoryRecordsForECR( d_season, assoc );

    // Create new standings for all those where we have standings for the last year
    //
    if ( listEcrLastYear != null )
      for ( GECRStandings ecrLast : listEcrLastYear )
        mapEcrNew.put( ecrLast.getClub(), new GECRStandings( ecrLast, d_season ) );

    if ( enrLastYear != null )
      enrNew = new GENRStandings( enrLastYear, d_season );

    // Loop over club/contest history records and add points to newly created standings.
    // Save ENR points in temporary member variable and increase variable that counts the
    // number of clubs having acquired ENR points this season.
    //
    for ( GClubHistoryRecord chr : listClubHistoryRecords )
    {
      GECRStandings ecrNew = mapEcrNew.get( chr.getClub() );

      // If we don't have a standing for last year, create new standings objects
      //
      if ( ecrNew == null )
      {
        ecrNew = new GECRStandings( chr.getClub(), d_season );
        mapEcrNew.put( ecrNew.getClub(), ecrNew );
      }

      if ( enrNew == null )
        enrNew = new GENRStandings( assoc, d_season );

      // Update ECR standings and if necessary ENR standings too
      //
      chr.updateECRStandings( ecrNew );
      chr.updateENRStandings( enrNew );
    }

    // Finalise all ECR standings by calculating the totals and make them persistent.
    //
    for ( GECRStandings ecr : mapEcrNew.values() )
    {
      ecr.updateTotals();
      if ( ecr.getPointsTotal().compareTo( BigDecimal.ZERO ) > 0 )
      {
        d_dao.makePersistent( ecr );
        d_listAllECRStandings.add( ecr );
      }
    }

    // Finalise new ENR standing object and make it persistent.
    //
    if ( enrNew != null )
    {
      enrNew.updateTotals();
      if ( enrNew.getPointsTotal().compareTo( BigDecimal.ZERO ) > 0 )
      {
        d_dao.makePersistent( enrNew );
        d_listAllENRStandings.add( enrNew );
      }
    }
  }

  /**
   * Sorts all ECR standings that may have been created during previous calls to
   * {@link #updateRankings(GNationalAssociation)}. Clears the collection of newly
   * created rankings objects afterwards.
   */
  public void updateECRRanks()
  {
    Collections.sort( d_listAllECRStandings );

    int        nRank = 0;
    int        nNum  = 1;
    BigDecimal decPrevPoints = null;

    // We make sure that clubs with the same number of points get the same rank
    //
    for ( GECRStandings ecr : d_listAllECRStandings )
    {
      if ( decPrevPoints == null || decPrevPoints.compareTo( ecr.getPointsTotal() ) != 0 )
        nRank = nNum;

      ecr.setRank( nRank );

      decPrevPoints = ecr.getPointsTotal();
      nNum++;
    }

    d_listAllECRStandings.clear();
  }

  /**
   * Sorts all ENR standings that may have been created during previous calls to
   * {@link #updateRankings(GNationalAssociation)}. Clears the collection of newly
   * created rankings objects afterwards.
   */
  public void updateENRRanks()
  {
    Collections.sort( d_listAllENRStandings );

    int        nRank = 0;
    int        nNum  = 1;
    BigDecimal decPrevPoints = null;

    // We make sure that nations with the same number of points get the same rank
    //
    for ( GENRStandings enr : d_listAllENRStandings )
    {
      if ( decPrevPoints == null || decPrevPoints.compareTo( enr.getPointsTotal() ) != 0 )
        nRank = nNum;

      enr.setRank( nRank );

      decPrevPoints = enr.getPointsTotal();
      nNum++;
    }

    d_listAllENRStandings.clear();
  }
}
